package org.framework.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类加载器
 *
 * 获取类加载器，加载类，获取指定包名下的所有类
 * @Author xuelongjiang
 */
public class ClassUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);


    /**
     * 获取类加载器
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 加载类
     *
     */
    public static Class<?> loadClass(String className,boolean isInitialized){
            Class<?> cls;
            try {
                cls = Class.forName(className,isInitialized,getClassLoader());
            }catch (Exception e){
                LOGGER.error("load class failure:",e);
                throw  new RuntimeException(e);
            }
            return cls;
    }


    public static Class<?> loadClass(String className){
        return  loadClass(className,false);
    }


    /**
     *
     *获取指定包名下的所有类
     *
     */
        public static Set<Class<?>> getClassSet(String packageName){
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            try {
                Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".","/"));
                while (urls.hasMoreElements()){
                    URL url = urls.nextElement();
                    if(url != null){
                        String protocol = url.getProtocol();
                        if(protocol.equals("file")){
                            //TODO 看一下结果，调试一下 %20
                            String packagePath = url.getPath().replaceAll("%20","");
                            addClass(classSet,packagePath,packageName);
                        }else if(protocol.equals("jar")){
                            JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();
                            if(jarURLConnection != null){
                                JarFile jarFile = jarURLConnection.getJarFile();
                                if(jarFile != null){
                                    Enumeration<JarEntry> jarEntries = jarFile.entries();
                                    while (jarEntries.hasMoreElements()){
                                        JarEntry jarEntry = jarEntries.nextElement();
                                        String jarEntryName = jarEntry.getName();
                                        if(jarEntryName.endsWith(".class")){
                                            String className = jarEntryName.substring(0,jarEntryName.lastIndexOf(".")).replaceAll("/",".");
                                            doAddClass(classSet,className);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }catch (Exception e){
                LOGGER.error("get class set failure",e);
            }
            return classSet;
        }


        public static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){

            File [] files = new File(packagePath).listFiles(new FileFilter() {
                public boolean accept(File file) {
                    return (file.isFile()&& file.getName().endsWith(".class"))||file.isDirectory() ;
                }
            });

            for(File file :files){
                String fileName = file.getName();
                if(file.isFile()){
                    String className = fileName.substring(0,fileName.lastIndexOf("."));
                    if(StringUtil.isNotEmpty(className)){
                        className = packageName + "."+className;
                    }
                    doAddClass(classSet,className);
                }else{
                    String subPackagePath = fileName;
                    if(StringUtil.isNotEmpty(packagePath)){
                        subPackagePath = packagePath+"/"+subPackagePath;
                    }
                    String subPackgeName = fileName;
                    if(StringUtil.isNotEmpty(packageName)){
                        subPackgeName = packageName+"."+subPackgeName;
                    }
                    addClass(classSet,subPackagePath,subPackgeName);
                }
            }
        }


        private static void doAddClass(Set<Class<?>> classSet,String className){

            Class<?> cls = loadClass(className,false);
            classSet.add(cls);
        }
}